#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <time.h>
#include "trie.c"
#include "mpi.h"

#define BLOCOS_POR_NO 10  

int main(int argc, char **argv){

	int rank, size;
	int resultAux =0;
	MPI_Status status;
    	
    char c, palavra[50];
	int i=0, j=0, k=0, letra, tamanhoPalavra=3, qtPalavras=0, palalavrasPorBloco=0, auxtam, qtTotal, aux=0, palavrasPorBloco, extrawords;
	//int totalWords=0;
	int qtTotalPalavras=0;
	int result;
	
	int qtPalavrasRead=0;
	int NodeSend=1; 		/*Node para envio das palavras*/
	int lastindex=0;
	int memblock=0;
	int memblockLastNode=0;
	int key=1;

	char *BufferFile;	/*Buffer total alocado para o arquivo*/
	char *BufferBlock;	/*Buffer menor alocado para cada bloco do arquivo*/

	long fileSize;		/*Tamanho do arquivo total*/
	
	int extraPalavras=0;
	float  faltando=0.9;
	time_t start,end;
	FILE *file;


	MPI_Init(&argc, &argv);
	MPI_Comm_size(MPI_COMM_WORLD, &size);
	MPI_Comm_rank(MPI_COMM_WORLD, &rank);
		
	if (rank==0){
		file=fopen("teste.txt", "r");
		if (file==NULL){
			printf("Erro ao abrir o arquivo.\n");
			exit(1);
		}
		time(&start);
		
		fseek (file, 0, SEEK_END);
		fileSize= ftell(file);
		fseek(file,0,SEEK_SET);	
		
		BufferFile = (char*) malloc (fileSize*sizeof(char));

		if(BufferFile != NULL){
      		// vamos copiar o conteúdo do arquivo para o buffer
      		size_t resultado = fread(BufferFile, 1, fileSize,file);

      		// vamos verificar se a operação foi feita com sucesso
      		if(resultado == fileSize)
        		printf("O conteudo do arquivo foi lido com sucesso\n\n");
		}
		
		fseek(file,0,SEEK_SET);	
		
		while(i<fileSize){
			c = BufferFile[i];
			if(c ==' ' ||  c ==',' || c =='.' || c =='!' || c =='?' || c=='\n' || c=='-' || c=='\0')
				qtPalavras++;
			i++;
		}
		time(&end);
		printf("TEMPO: %.2lf\nQuantidade total de palavras: %d\n", difftime(end,start), qtPalavras);
		
		/* Caso consiga dividir as palavras do arquivo em blocos inteiros*/
		if(qtPalavras % (size-1) == 0)
		{
			palavrasPorBloco = qtPalavras/(size-1);		
		}
		else /* Joga o final das palavras em um bloco final*/
		{
			palavrasPorBloco = qtPalavras/(size-1);
			extraPalavras = qtPalavras - palavrasPorBloco*(size-1);
		}		
		printf("quantidade de palavras por Bloco = %d\n + quantidade Extra de palavras no ultimo bloco = %d\n",palavrasPorBloco,extraPalavras);
	
	}
		MPI_Bcast(&palavrasPorBloco, 1, MPI_INT, 0, MPI_COMM_WORLD);
    	MPI_Bcast(&extraPalavras, 1, MPI_INT, 0, MPI_COMM_WORLD);

	if(rank == 0){	
		/* Envio de Palavras */

		/* Comeca i=1 pois node comeca em 1 */
		i=0;
		while(NodeSend < size){
			c = BufferFile[i];
			if(c ==' ' ||  c ==',' || c =='.' || c =='!' || c =='?' || c=='\n' || c=='-' || c=='\0')
				qtPalavrasRead++;

			/*Caso completa o numero de palavras lidas e nao eh o ultimo bloco a ser enviado*/
			if(qtPalavrasRead == palavrasPorBloco && NodeSend != size-1){
				/*Envio das palavras para determinado bloco*/
				/*Aloca o BufferBloco a ser enviado*/
				memblock = i - lastindex;
				BufferBlock = (char*)malloc( (memblock+1)*sizeof(char));
				//memcpy(BufferFile,BufferBlock[],i);

				if(BufferFile[i] == '\0'){
					for(k=0,j=lastindex;j<=i;j++,k++)
						BufferBlock[k]=BufferFile[j];
					//printf("\n\nBufferblock = %s\n",BufferBlock);		
				}
				else{
					for(k=0,j=lastindex;j<i;j++,k++)
						BufferBlock[k]=BufferFile[j];	
				
					BufferBlock[k]='\0';	
					//printf("\n\nBufferblock = %s\n",BufferBlock);		
				}

				MPI_Send(BufferBlock, memblock+1, MPI_CHAR, NodeSend, 0, MPI_COMM_WORLD);
				//printf("Sending to node= %d, mensagem: %s\n",NodeSend, BufferBlock);
				
				NodeSend++;	/*Proximo Node a ser enviado*/
				lastindex=i+1;
				qtPalavrasRead = 0;
				
			}
			/* Caso seja o ultimo node completa com as restantes palavras do file*/
			else if(NodeSend == size-1)
			{
				
				/*copia o restante das palavras para o ultimo bloco*/
				memblockLastNode = fileSize - i;
				//printf("memblockLastNode = %d\n\n",memblockLastNode);	
				BufferBlock = (char*)malloc(memblockLastNode*sizeof(char));
				
				for(k=0,j=lastindex;j<fileSize;j++,k++)
				{
					BufferBlock[k]=BufferFile[j];
				}
				BufferBlock[memblockLastNode-1]='\0';
				//printf("\nlast node send BufferBlock= %s\n\n",BufferBlock);
				MPI_Send(BufferBlock, memblockLastNode, MPI_CHAR, NodeSend, 0, MPI_COMM_WORLD);	
				//printf("Sending to node= %d, mensagem: %s\n",NodeSend, BufferBlock);

				NodeSend++;	/*Proximo Node a ser enviado*/
				lastindex=i+1;
				qtPalavrasRead = 0;
			}
			i++;
		}

		free(BufferFile);
		free(BufferBlock);
	}
	
	MPI_Bcast(&memblock, 1, MPI_INT, 0, MPI_COMM_WORLD);
	MPI_Bcast(&memblockLastNode, 1, MPI_INT, 0, MPI_COMM_WORLD);

	if(rank != 0){
		int multword;
		char *message, palavra[50], palavraAux[50];
		int k;
		trie *PALAVRAS;
		PALAVRAS = trie_new();

		if(rank!=size-1){
			message = (char*)malloc((memblock+100)*sizeof(char));
			MPI_Recv(message, memblock+100, MPI_CHAR, 0, 0, MPI_COMM_WORLD,&status);
		}
		else{
			message = (char*)malloc(memblockLastNode*sizeof(char));	
			MPI_Recv(message, memblockLastNode, MPI_CHAR, 0, 0, MPI_COMM_WORLD,&status);
		}
		srand(time(NULL));
		
		time (&start);			
		extrawords = 0;
		multword = 0;
		k=0;
		while(message[k] !='\0'){
		    if(message[k]==' '){
		      multword++;
		      k++;
		    }
		    else k++;
		}
		  
		while(key == 1){		  
			if(message[aux] !=' ' && message[aux] !=',' && message[aux] !='.' && message[aux] !='!' && message[aux] !='?' && message[aux]!='\n' && message[aux]!='-' && message[aux]!='\0'){
				if((message[aux]>=65 && message[aux]<=90)||(message[aux]>=97 && message[aux]<=122))
					palavra[i++] = tolower(message[aux]);
			}
			else if(i!=0){
				palavra[i] = '\0';
				if(i<=5)
					if(trie_lookup(PALAVRAS, palavra)==0){
						trie_insert(PALAVRAS, palavra);
						//printf("Node: %d, Inserido: %s",rank, palavra);
					}
				else{
					j=0;
					//Separa a palavra em grupos de 3, e o restante, caso necessario
					while(j<i){
						for(k=0;k<tamanhoPalavra;k++)
							palavraAux[k]=palavra[j++];
						palavraAux[tamanhoPalavra]='\0';
						if(trie_lookup(PALAVRAS, palavraAux)==0){
							trie_insert(PALAVRAS, palavraAux);
							//printf("Node: %d, Inserido: %s\n",rank, palavra);
						}
						auxtam=i-j;
						if(auxtam==4)
							tamanhoPalavra=4;
						if(auxtam<3)
							tamanhoPalavra=auxtam;
					}	
				}
				if(message[aux] == '\0')
					key = 0;
				//Zera o contador para começar a ler a proxima palavra
				i=0;
				//Redefine o tamanho default de 3
				tamanhoPalavra=3;
			}
			aux++;
		}
		
		extrawords = 0;
		time (&end);
		printf("---Termino do processamento de palavras no node: %d.\n---Tempo: %.2lf\n---Quantidade total de palavras: %d\n", rank, difftime(end,start), trie_num_entries(PALAVRAS));
		
		time (&start);
		//Gera palavras e tenta consumir, enquanto tiverem palavras não geradas
		while(trie_num_entries(PALAVRAS)>0){
			//Determina o tamanho da palavra a ser gerada. Varia de 1 a 5
			tamanhoPalavra = rand()%5+1;
			
			//Gera a palavra do tamanho dete rminado
			for(i=0;i<tamanhoPalavra;i++){	
				letra = rand()%26+97;
				palavra[i]=letra;
			}
			palavra[i]='\0';
			
			//verifica se a palavra existe e remove
			if(trie_lookup(PALAVRAS, palavra)!=0)
				trie_remove(PALAVRAS, palavra);

		}
		MPI_Send(&qtTotalPalavras, 1, MPI_INT, 0, 0, MPI_COMM_WORLD);
		trie_delete(PALAVRAS);
	}
	if(rank==0){
	  resultAux=1;
	  while(resultAux < size-1){
	      result = 0;
	      MPI_Recv(&result,       //buffer da mensagem
			1,             //quantidade de itens
		      MPI_INT,	    //de tipo int
		      MPI_ANY_SOURCE,//recebe de qualquer source
		      MPI_ANY_TAG,   //qualquer TAG de mensagem
		      MPI_COMM_WORLD,//comunicador default
		      &status);      //infos
	      resultAux++;
	      time(&end);
	      //printf("resultAux = %d\n",resultAux);
	      printf("\nTerminado: %.2lf%% em %.2lf segundos\n", (double)(((double)resultAux/(size-1))*100), difftime (end,start)); 
	     }
	}
	MPI_Finalize();
return 0;
}
